# 包含了数据爬取和区分的函数
import pickle
import string
from collections import defaultdict
from pathlib import Path

import jieba
import requests
import joblib

BASE_DIR = Path(__file__).resolve().parent.parent
DATA_DIR = BASE_DIR / 'data'

clf = joblib.load(DATA_DIR / "lr.model")

with open(DATA_DIR / "tf_idfs.pkl", 'rb') as f:
    tf_idfs = pickle.load(f)

with open(DATA_DIR / "word_idf.pkl", 'rb') as f:
    word_idf = pickle.load(f)

with open(DATA_DIR / "wordsfre.pkl", 'rb') as f:
    wordsfre = pickle.load(f)


def bv_predictor():
    saved_words = wordsset(DATA_DIR / "reservedwords.txt")
    result = None
    while True:
        bv = yield result
        if not bv:
            return

        dic = get_all(bv)
        result = dic
        if dic['aid']:
            line = dic['title'] + ' ' + dic['desc'] + ' '.join(dic['reviews'])

            line = list(jieba.cut(line, cut_all=False))
            line = rmove_sw(line, saved_words)

            # 计算x的tf-idf
            words_dic = defaultdict(int)
            for word in line:
                words_dic[word] += 1
            word_tf = {}
            for k in words_dic:
                word_tf[k] = words_dic[k] / sum(words_dic.values())
            tf_idf = {}
            for k in word_tf:
                if word_idf.get(k, None) is not None:
                    tf_idf[k] = word_tf[k] * word_idf[k]

            x = []
            # 计算特征向量
            for j in range(1000):
                word = wordsfre[j][0]
                if tf_idf.get(word, None) is not None:
                    x.append(tf_idf[word])
                else:
                    x.append(0)

            reviews = result.pop("reviews")
            for i, review in enumerate(reviews, start=1):
                result[f"review{i}"] = review
            result["result"] = "是" if determin(x) else "否"


# 读入词表的text文件
def wordsset(path):
    # 读入stopword的text文件
    file = open(path, encoding='utf8')
    lines = file.readlines()
    # 将词放入列表中
    words = []
    for word in lines:
        word = word.replace('\ufeef', '').strip()
        words.append(word)
    return words


def rmove_sw(word_list, savewords=None):
    # new_words是去除停用词后的新词表
    if savewords is None:
        savewords = set()
    new_words = []

    stop_words = stopword_set()

    punc = string.punctuation

    for i in word_list:
        if i in savewords:
            new_words.append(i)
        if len(i) == 1 and i.isalpha():
            continue
        if i in [' ', '⠀', '…', '▓', '░', '█']:
            continue
        if i not in stop_words:
            if is_number(i):  # 去除全为数字的词
                pass
            else:
                # 去除全为标点的词如 “--------”
                for j in range(len(i)):
                    if i[j] not in punc:
                        new_words.append(i)
                        break
    return new_words


def determin(x):
    x = [x]
    y = clf.predict(x)
    return y[0]


def get_info(bv, isP=0):
    """
    获取bv号对应视频的相关的基本信息
    Input: bv是视频的bv号可以唯一标识视频，isP是否打印读取完成的信息
    return: 返回视频基本信息
    """

    # 设置headers，解决b站反爬限制
    headers = {
        'Host': 'api.bilibili.com',
        'Referer': 'https://www.bilibili.com/video/av77413543',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'
    }
    # 获取视频信息
    url = r"https://api.bilibili.com/x/web-interface/view?bvid=" + bv
    info_rsp = requests.get(url=url, headers=headers)
    info_json = info_rsp.json()

    dic = dict()
    if info_json['code'] == 0:
        # av号，bv号，标题，分类，简介，播放量，弹幕量，点赞数，投币数，收藏数，分享数
        dic['aid'] = info_json['data']['aid']
        dic['bv'] = bv
        dic['time'] = info_json['data']['pubdate']

        dic['title'] = info_json['data']['title']
        dic['tname'] = info_json['data']['tname']
        dic['desc'] = info_json['data']['desc']

        dic["view"] = info_json['data']['stat']['view']
        dic["danmaku"] = info_json['data']['stat']['danmaku']
        dic["like"] = info_json['data']['stat']['like']
        dic["coin"] = info_json['data']['stat']['coin']
        dic["collect"] = info_json['data']['stat']['favorite']
        dic["share"] = info_json['data']['stat']['share']

        # 获取up主信息
        dic['up'] = info_json['data']['owner']['name']
        dic['mid'] = info_json['data']['owner']['mid']

        # 打印完成信息
        if isP == 1:
            print("--Sucess: " + bv + "-info get")
    else:
        dic['aid'] = 0
        dic['mid'] = 0

    return dic


def get_up(dic, isP=0):
    """
    获取up主对应的粉丝数
    Input: dic从get_info中获取的信息字典，isP是否打印读取完成的信息
    return: 返回新增粉丝数和观看数的信息
    """
    # 设置headers
    headers = {
        'Host': 'api.bilibili.com',
        'Referer': 'https://www.bilibili.com/video/av77413543',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'
    }

    UUID = str(dic['mid'])
    url2 = r"https://api.bilibili.com/x/relation/stat?vmid=" + UUID + "&jsonp=jsonp"
    info_rsp = requests.get(url=url2, headers=headers)
    info_json = info_rsp.json()

    if info_json['code'] == 0:
        # up主粉丝数
        dic['upfans'] = info_json['data']['follower']
        if isP == 1:
            print("--Sucess: " + dic['bv'] + "-up info get")
    else:
        print("--Error: " + dic['bv'] + "-无法获取up主信息")

    return dic


def get_review(dic, reviews=3, isP=0):
    """
    获取视频部分评论等
    Input: dic从get_info中获取的信息字典，reviews想获取的评论数，默认选取最前面的三条评论，isP是否打印读取完成的信息
    return: 返回新增评论数和部分评论的信息
    """
    # 设置headers
    headers = {
        'Host': 'api.bilibili.com',
        'Referer': 'https://www.bilibili.com/video/av77413543',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'
    }

    aid = str(dic['aid'])
    url3 = r"https://api.bilibili.com/x/v2/reply?jsonp=jsonp&pn=1&type=1&oid=" + \
           aid + "&sort=1&nohot=1"
    info_rsp = requests.get(url=url3, headers=headers)
    info_json = info_rsp.json()

    if info_json['code'] == 0:
        # 视频评论数和前三条评论（如果有的话）
        review = info_json['data']['replies']
        dic['review_count'] = info_json['data']['page']['count']
        dic['review_acount'] = info_json['data']['page']['acount']

        rs = []
        for i in range(reviews):
            review.append({"content": {"message": ""}})
            rs.append(review[i]['content']['message'])

        dic['reviews'] = rs
        if isP == 1:
            print("--Sucess: " + dic['bv'] + "-review info get")
    else:
        print("--Error: " + dic['bv'] + "-无法获取up主信息")

    return dic


def get_all(bv):
    dic = get_info(bv)
    if dic['aid']:
        get_up(dic)
        get_review(dic)
    return dic


def stopword_set():
    # 读入stopword的text文件
    stopword_file = open(DATA_DIR / "hit_stopwords.txt", encoding='utf8')
    stopword_lines = stopword_file.readlines()

    # 将停用词放入列表中
    stopwords = []
    for stop_word in stopword_lines:
        stop_word = stop_word.replace('\ufeef', '').strip()
        stopwords.append(stop_word)
    # 将停用词用集合表示

    return stopwords


# 判断字符是否为数字
def is_number(s):
    try:
        float(s)
        return True
    except ValueError:
        pass

    try:
        import unicodedata
        unicodedata.numeric(s)
        return True
    except (TypeError, ValueError):
        pass

    return False


if __name__ == "__main__":
    p = bv_predictor()
    p.send(None)
    result = p.send(input("请输入BV: "))
    print(result)
